package it.polimi.jita.cp.block.dd;

import it.polimi.jita.cp.block.de.Histogram;
import it.polimi.jita.cp.block.de.Histogram.Bin;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.math3.distribution.GammaDistribution;
import org.apache.commons.math3.distribution.RealDistribution;

public class NewVirtualMachineData extends AbstractVirtualMachineData {
	private RealDistribution timeOffDistribution = new GammaDistribution(5d,
			8640d);

	private Histogram demandHistogram;
	private Bin currentBin;
	private Iterator<Bin> binsIterator;
	private StateProbability sp = new StateProbability();

	protected NewVirtualMachineData(String name, Integer vCpuNum) {
		super(name, vCpuNum, new ArrayList<Instant>());
	}

	protected Instant addInstant() {
		setCurrentInstant(new Instant(0d, 0d, 0d));
		getInstants().add(getCurrentInstant());
		sp.increment(timeOffDistribution,
				VirtualMachineData.TIME_ON_DISTRIBUTION);
		// System.out.println(">>>>>>>>>>>pOn: " + sp.getpOn());
		return getCurrentInstant();
	}

	protected Bin nextBin() {
		if (binsIterator != null) {
			currentBin = binsIterator.next();
		} else {
			resetBinIterator();
			currentBin = binsIterator.next();
		}

		return currentBin;

	}

	protected boolean hasNextBin() {
		if (binsIterator != null) {
			return binsIterator.hasNext();
		} else {
			resetBinIterator();
			return binsIterator.hasNext();
		}
	}

	protected void resetBinIterator() {
		binsIterator = demandHistogram.getBins().iterator();
	}

	@Override
	public Double getDemand() {
		if (DynamicDecisionMaker.PROBABILISTIC_METHOD && isLikelyDemand()) {
			return currentBin.getUpperBound().doubleValue()
					* timeOffDistribution.cumulativeProbability(getInstants()
							.size()) * getvCpuNum();
			//
			// return currentBin.getUpperBound().doubleValue() * sp.getpOn()
			// * getvCpuNum();
		}
		return currentBin.getUpperBound().doubleValue() * getvCpuNum();
	}

	protected void setDemandHistogram(Histogram demandHistogram) {
		this.demandHistogram = demandHistogram;
	}

	private class StateProbability {
		Double pOn = null;
		Double pOff = null;

		Map<Integer, Double> ons = new HashMap<>();
		Map<Integer, Double> offs = new HashMap<>();

		StateProbability() {
			offs.put(1, 1d);
		}

		Double getpOn() {
			if (pOn == null) {
				pOn = 0d;
				for (Entry<Integer, Double> e : ons.entrySet()) {
					pOn += e.getValue();
				}
			}
			return pOn;
		}

		Double getpOff() {
			if (pOff == null) {
				pOff = 0d;
				for (Entry<Integer, Double> e : offs.entrySet()) {
					pOff += e.getValue();
				}
			}
			return pOff;
		}

		private void increment(RealDistribution pOff, RealDistribution pOn) {

			Map<Integer, Double> onNew = new HashMap<>();
			onNew.put(1, 0d);

			Map<Integer, Double> offNew = new HashMap<>();
			offNew.put(1, 0d);

			for (Entry<Integer, Double> eOn : ons.entrySet()) {
				Integer newId = eOn.getKey() + 1;
				onNew.put(newId, (1 - pOn.cumulativeProbability(eOn.getKey()))
						* eOn.getValue());
			}

			for (Entry<Integer, Double> eOff : offs.entrySet()) {
				Double onZeroToAdd = onNew.get(1);
				double d = pOff.cumulativeProbability(eOff.getKey());
				onZeroToAdd += pOff.cumulativeProbability(eOff.getKey())
						* eOff.getValue();
				onNew.put(1, onZeroToAdd);

				Integer newId = eOff.getKey() + 1;
				offNew.put(
						newId,
						(1 - pOff.cumulativeProbability(eOff.getKey()))
								* eOff.getValue());
			}

			ons.clear();
			ons.putAll(onNew);
			offs.clear();
			offs.putAll(offNew);

			this.pOn = null;
			this.pOff = null;

		}
	}
}
